# -*- coding: utf-8 -*-
# article_search.py
# Created by Hardy on 25th, Jan
# Copyright 2017 杭州网川教育有限公司. All rights reserved.

from querier.esquerier import ElasticSearchQuerier
import utils.utils as utils


READ = 'read_num'
LIKE = 'like_num'
RATIO = 'like_read_ratio'
RELATIVE = 'relative'

MINIMUM_SHOULD_MATCH = '5<85% 10<9'
CATEGORY_CUTOFF = 0.9
MAX_KEYWORDS = 100


class WechatKOLMatchBackendQuerier(ElasticSearchQuerier):
    def __init__(self, es, index, doc_type, nlp_service=None):
        super(WechatKOLMatchBackendQuerier, self).__init__(es, index, doc_type)
        self.nlp_service = nlp_service

    def _build_query(self, args):
        """输入 文案, 节日, 热点事件, 类目, 返回匹配KOL"""

        # 文案
        term = args.get('term', '')
        term = term if term else ''
        filter_keywords = args.get('filter_keywords', [])
        filter_keywords = filter_keywords if filter_keywords else []

        # 节气
        festivals = args.get('festivals')
        festivals = festivals if festivals else []
        festival_keywords = [utils.get_festival_keywords(f) for f in festivals]

        # fixme specify festival keywords
        # 需要对标题和关键词过滤
        hot_events = args.get('hot_events')
        hot_events = hot_events if hot_events else []
        hot_events = [f.strip() for f in hot_events]

        # category in filters
        filters = args.get('filters', {})
        if filters is None:
            filters = {}
        order = args.get('order_by', utils.ORDER_OVERALL)
        from_ = args.get('from', 0)
        size_ = args.get('size', 10)
        highlight = args.get('highlight', False)

        # 处理查询文本
        term2, keywords, ex_keywords, weights = utils.process_query_term(term, self.nlp_service, 'keywords', allowed_num_words=MAX_KEYWORDS)

        query = self._genQuery(' '.join(keywords[0:MAX_KEYWORDS]), filter_keywords, hot_events, festival_keywords, filters, order, from_, size_, highlight)
        # keywords = [k[0] for k in sorted(h_kv.items(), key=operator.itemgetter(1))]
        return query, {}, {'keywords': keywords, 'order': order}

    def _build_result(self, es_result, param):
        # keywords = param['keywords']
        order = param['order']
        total = es_result['hits']['total']
        max_score = es_result['hits']['max_score']
        wechat = []

        for hit in es_result['hits']['hits']:
            wechat.append(self.extractResult(hit, max_score, order))

        return {
            'total': total,
            'wechat': wechat,
        }

    def _genQuery(self, query_keywords, filter_words, hot_events, festival_keywords, filters, order, from_, size_, highlight):
        filter_clause = []
        if filters:
            if filters.get('category'):
                filters['category'] = [utils.category_smzdm_2_encode(c) for c in filters['category']]
                filter_clause = self._addFilterClause(filter_clause, filters, 'category', 'should')
                filters['category_weight'] = [CATEGORY_CUTOFF]
                filter_clause = self._addFilterRangeClause(filter_clause, filters, 'category_weight')

            if filters.get('category_media'):
                filters['category_media'] = [utils.category_media_2_encode(c) for c in filters['category_media']]
                filters['category_media_weight'] = [CATEGORY_CUTOFF]
                filter_clause = self._addFilterClause(filter_clause, filters, 'category_media', 'should')
                filter_clause = self._addFilterRangeClause(filter_clause, filters, 'category_media_weight')

            if filters.get('score'):
                filters['score_cal'] = filters['score']
                filter_clause = self._addFilterRangeClause(filter_clause, filters, 'score_cal')
            filter_clause = self._addFilterClause(filter_clause, filters, 'keywords')
            filter_clause = self._addFilterClause(filter_clause, filters, 'customer_type', 'should')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'sum_read_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'avg_read_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'max_read_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'sum_like_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'avg_like_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'max_like_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'like_read_ratio')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'timestamp')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'orig_ratio')
            filter_clause = self._addFilterMatch(filter_clause, filters, 'biz_code')
            filter_clause = self._addFilterMatch(filter_clause, filters, 'biz_name')
            filter_clause = self._addFilterMatch(filter_clause, filters, 'biz_info')

        query = {"query": {"bool": {}}}

        for words in filter_words:
            filter_clause = self.filter_keywords(filter_clause, words)

        if filter_clause:
            query['query']['bool']['filter'] = filter_clause

        if query_keywords.strip():
                query['query']['bool']['must'] = {
                    'bool': {
                        'should': [
                            {
                                'match': {
                                    'biz_name_seg': {
                                        'analyzer': 'whitespace',
                                        'query': query_keywords,
                                        'boost': 2

                                    }
                                }
                            },
                            {
                                'match': {
                                    'biz_info_seg': {
                                        'analyzer': 'whitespace',
                                        'query': query_keywords,
                                        'boost': 2

                                    }
                                }
                            },
                            {
                                'match': {
                                    'keywords': {
                                        'analyzer': 'whitespace',
                                        'query': query_keywords,
                                        'boost': 1
                                    }

                                }
                            }
                        ]
                    }
                }

        query['sort'] = []

        if order == utils.ORDER_INFLUENCE:
            query['sort'] = [
                {'score_cal': 'desc'},
                {'_score': 'desc'}
            ]
        elif order == utils.ORDER_RELATIVE:
            query['sort'] = [
                {'_score': 'desc'},
                {'score': 'desc'}
            ]
        elif order == utils.ORDER_TIMESTAMP:
            query['sort'] = [
                {'timestamp': 'desc'},
                {'score': 'desc'}
            ]
        elif order == 'like_read_ratio':
            query['sort'] = [
                {'like_read_ratio': 'desc'},
                {'_score': 'desc'}
            ]
        elif order == 'avg_read_num':
            query['sort'] = [
                {'avg_read_num': 'desc'},
                {'_score': 'desc'}
            ]
        elif order == 'avg_like_num':
            query['sort'] = [
                {'avg_like_num': "desc"},
                {'_score': 'desc'}
            ]
        elif order == 'max_read_num':
            query['sort'] = [
                {'max_read_num': 'desc'},
                {'_score': 'desc'}
            ]
        elif order == 'max_like_num':
            query['sort'] = [
                {'max_like_num': 'desc'},
                {'_score': 'desc'}
            ]
        else:
            query['sort'] = [
                {
                    '_script': {
                        "type": "number",
                        "script": {
                            "lang": "painless",
                            "inline": "Math.log(_score + 1.0) * Math.log(doc.score.value + 0.001)"
                        },
                        "order": "desc"
                    },

                }
            ]

        if filters.get('category'):
            query['sort'] = [{'category_weight': 'desc'}] + query['sort']

        query['from'] = from_
        query['size'] = size_
        query['track_scores'] = True
        if highlight:
            query['highlight'] = {
                "pre_tags": ["<span class='keyword'>"],
                "post_tags": ["</span>"],
                "fields": {"keywords": {}, "biz_info_seg": {}, "biz_name_seg": {}}
            }
        else:
            query['highlight'] = {
                "pre_tags": [""],
                "post_tags": [""],
                "fields": {"keywords": {}, "biz_info": {}}
            }

        return query

    @staticmethod
    def filter_keywords(must_clause, keywords):
        clause = []
        must_clause.append({
            'bool': {'should': clause}
        })
        for fk in keywords:
            q = {
                'match_phrase': {
                    "biz_info": {
                        'query': fk,
                        'slop': 1,
                    },
                }}
            clause.append(q)

        q_keywords = {
            'multi_match': {
                'analyzer': 'whitespace',
                'query': ' '.join(keywords),
                'fields': ['keywords', 'biz_info_seg^3'],
                # 'minimum_should_match': ""
            }
        }
        clause.append(q_keywords)
        return must_clause

    def _addFilterMatch(self, must_clause, filters, key, cond='must'):
        if key in filters:
            if filters[key]:
                clause = []
                must_clause.append({
                    'bool': {cond: clause}
                })
                values = filters[key]
                if isinstance(values, str):
                    values = values.split(' ')
                for fk in values:
                    clause.append({'match': {key: {'query': fk, 'minimum_should_match': MINIMUM_SHOULD_MATCH}}})
        return must_clause

    def _addFilterClause(self, filter_clause, filters, key, cond='must'):
        if key in filters:
            if filters[key]:
                clause = []
                filter_clause.append({
                    'bool': {
                        cond: clause
                    }
                })
                for fk in filters[key]:
                    clause.append({'term': {key: fk}})
        return filter_clause

    def _addFilterRangeClause(self, filter_clause, filters, key):
        if key in filters:
            if filters[key]:
                clause = []
                filter_clause.append({
                    'bool': {
                        'must': clause
                    }
                })
                fk = filters[key]
                if not isinstance(fk, list) or len(fk) < 1:
                    pass
                else:
                    min_fk = fk[0]
                    if len(fk) >= 2:
                        max_fk = fk[1]
                    else:
                        max_fk = None
                    if min_fk is not None and min_fk != 'null':
                        clause.append({'range': {key: {"gte": min_fk}}})
                    if max_fk is not None and max_fk != 'null':
                        clause.append({'range': {key: {"lte": max_fk}}})
        return filter_clause

    def extractResult(self, hit, max_score, order, in_keywords=[]):
        source_ = hit['_source']
        biz_info = source_['biz_info']

        kol_avatar_url = source_['head_img']
        kol_influence_score = source_['score_cal']
        biz_code = source_['biz_code']
        biz_name = source_['biz_name']
        qr_template = "http://mp.weixin.qq.com/mp/qrcode?scene=10000004&size=102&__biz=%s&mid=%s&idx=%s&sn=%s&send_time="
        bid = source_.get('bid')
        mid = source_.get('mid')
        idx = source_.get('idx')
        sn = source_.get('sn')
        qrcode_url = qr_template % (bid, mid, idx, sn)
        score_ = hit['_score'] #/ (len(in_keywords) + 1)

        keywords = source_['keywords'][0:10]
        h_keywords = keywords
        highlight = hit.get('highlight')
        if highlight:
            h_kol_name = highlight.get('biz_name')
            biz_name = biz_name if not h_kol_name else h_kol_name[0]
            h_kol_info = highlight.get('biz_info')
            biz_info = biz_info if not h_kol_info else h_kol_info[0]
            h_keywords = highlight.get('keywords')
            if h_keywords:
                hk2 = [s.replace("<span class='keyword'>", '').replace("</span>", '') for s in h_keywords]
                if hk2:
                    h_keywords += [k for k in keywords if k not in hk2][0:10]

        # cat = utils.get_class0(source_['categories'], source_['categories_weight'])

        keywords = source_['keywords']
        term_in_keywords = [i for i in keywords if i in in_keywords]
        len_term_in_keywords = len(term_in_keywords)
        customer_type = utils.customer_type_dict.get(source_.get('customer_type'))
        orig_ratio = source_['orig_ratio']
        keywords_weight = source_['keywords_weight']

        kw_dict = dict(zip(keywords, keywords_weight))

        return {
            'biz_name': biz_name,
            'biz_code': biz_code,
            'biz_info': biz_info,
            'qrcode_url': qrcode_url,
            'keywords': keywords,
            'keywords_weight': keywords_weight,
            'head_img': kol_avatar_url,
            'score': kol_influence_score,
            'relative': 1 if max_score <= 0 else score_ / max_score,
            # 'max_score': [ max_score],
            'sum_read_num': source_['sum_read_num'],
            'avg_read_num': source_['avg_read_num'],
            'max_read_num': source_['max_read_num'],
            'sum_like_num': source_.get('sum_like_num'),
            'avg_like_num': source_.get('avg_like_num'),
            'max_like_num': source_.get('max_like_num'),
            'like_read_ratio': source_.get('like_read_ratio'),
            'categories': [utils.category_smzdm_2_decode(int(c)) for c in source_['categories']],
            'categories_media': [utils.category_media_2_decode(c) for c in source_['categories_media']],
            'timestamp': source_.get('timestamp'),
            'orig_ratio': source_.get('orig_ratio'),
            'orig_count': source_.get('orig_count'),
            'article_count': source_.get('article_count'),
            'media_name': source_.get('media_name'),
            'term_in_keywords': term_in_keywords,
            'term_in_keywords_weight': [kw_dict.get(t) for t in term_in_keywords],
            'len_term_in_keywords': len_term_in_keywords,
            'customer_type': customer_type,

        }
